import {
    _decorator,
    Animation,
    Component,
    director,
    instantiate,
    macro,
    Node,
    Prefab,
    ProgressBar,
    resources,
    Sprite,
    SpriteFrame,
    UITransform,
    Vec2,
    Vec3
} from 'cc';
import {GameEvent} from '../../game-event';
import {DamageNumber,} from "db://assets/script/game/battle/damage-number";
import {lobby} from "db://assets/script/lobby";
import {userInfo} from "db://assets/script/user-info";
import {FishGameStatus} from "db://assets/script/game/constants/fish-game-status";
import {FishDoSomethingEvent} from "db://assets/script/common/events/fish-events/fish-do-something-event";
import {FishShow} from "db://assets/script/game/fish/fish-show";
import {BattleInGame} from "db://assets/script/game/battle/battle-in-game";
import {NormalAttackComp} from "db://assets/script/game/skills/normal-attack-camp";
import {BattleEndEvent} from "db://assets/script/common/events/battle-end-event";
import {FishGameAction} from "db://assets/script/game/constants/fish-game-action";
import {FreshImmortal} from "db://assets/proto/shared/v1/wuni";
import {
    FishAttackRecordReply,
    FishBattleCompleteReply,
    FishDeadReply,
    FishEarnReply,
    FishExploreReply,
    FishGrowthReply,
    FishMagicPowerRecoverReply,
    FishMoveCompleteReply,
    FishRecoverReply,
    FishStayReply
} from '../../../proto/new-world-ws/v1/internal';
import {DamageType} from "db://assets/script/game/battle/damage-type";
import {FishCoreChangeEvent} from "db://assets/script/common/events/fish-events/fish-core-change-event";
import {FishInfoComp} from "db://assets/script/game/fish/fish-info-comp";
import {fishUtils} from "db://assets/script/utils/fish-utils";
import {UserFishListenEvent} from "db://assets/script/common/events/fish-events/user-fish-listen-event";
import {UserFishAddOthersEvent} from "db://assets/script/common/events/fish-events/user-fish-add-others-event";
import {configsHelper} from "db://assets/script/configs-helper";
import {ClearFishEvent} from "db://assets/script/common/events/clear-fish-event";
import Long from "long";
import {FishStatus} from "db://assets/proto/shared/v1/biz";
import {FishBeginMoveEvent} from "db://assets/script/common/events/fish-events/fish-begin-move-event";
import {FishBattleWalkEvent} from "db://assets/script/common/events/fish-events/fish-battle-walk-event";
import {utils} from "db://assets/script/utils/utils";
import {FishBeAttackShockEvent} from "db://assets/script/common/events/fish-events/fish-be-attack-shock-event";
import {serverTimeHelper} from "db://assets/script/server-time-helper";
import {cmm} from "../../utils/cmm";
import maxNormalHealth = fishUtils.maxNormalHealth;
import {propsConfigsLoader} from "db://assets/script/configs/props-configs-loader";

const {ccclass, property} = _decorator;

class FishMoveTask {
    targetPos: Vec3
    speed: number
    linerDamping: number
    changeFaceToward: boolean
    moveCallback: () => void
    stopped: boolean
    paused: boolean
    completeType: number
    moveDuration: number
    moveDurationRemain: number
    beginPos: Vec3

    constructor(beginPos: Vec3, targetPos: Vec3, speed: number, linerDamping: number, changeFaceToward: boolean, completeType: number, moveDuration: number, moveCallback: () => void) {
        this.targetPos = targetPos;
        this.speed = Math.min(1000, speed);
        this.linerDamping = linerDamping;
        this.moveCallback = moveCallback;
        this.stopped = true;
        this.paused = false;
        this.completeType = completeType;
        this.changeFaceToward = changeFaceToward;
        this.moveDuration = moveDuration;
        this.moveDurationRemain = moveDuration;
        this.beginPos = beginPos;
    }

    start() {
        this.stopped = false;
    }

    stop() {
        this.stopped = true;
    }

    pause() {
        this.paused = true;
    }

    recover() {
        this.paused = false;
    }

}

@ccclass('FishInPool')
export class FishInPool extends Component {

    @property(ProgressBar)
    progressbar!: ProgressBar;

    @property(Sprite)
    realmShow!: Sprite

    @property(FishShow)
    fishShow!: FishShow

    mine: boolean = true;
    wander: boolean = false;
    inClear: boolean = false;
    fishId!: Long;
    zoneId!: Long;

    gameState: FishGameStatus = FishGameStatus.EMPTY
    gameAction: FishGameAction = FishGameAction.NOTING
    _fakeExploreCountDown: number = 0
    beginX: number = 0
    battleTarget: FishInPool | null = null
    moveTasks: FishMoveTask[] = []
    speed: number = 100

    latestMoveAtMs: number = 0
    currentMoveTask: FishMoveTask | null = null
    _screenWidth: number = 0;
    _screenHeight: number = 0;


    start() {
        this.onEvent()
        // this.rigidBody = this.getComponent(RigidBody2D)!
    }

    init(fish: FreshImmortal, mine: boolean, wander: boolean, screenW: number, screenH: number): void {
        this.wander = wander
        this.mine = mine
        this.fishId = fish.id
        this.zoneId = fish.zoneId
        this.fishShow.init(this.fishId, fish.masterInfo!.masterId == "0" ? 2 : 1)
        this.gameAction = FishGameAction.NOTING
        this.beginX = this.node.x
        this._screenWidth = screenW
        this._screenHeight = screenH
        if (this.wander) {
            director.emit(GameEvent.userFishAddOthers, new UserFishAddOthersEvent(fish))
            this.listenSelf()
            this.schedule(() => this.listenSelf(), 50, macro.REPEAT_FOREVER)
        }
        this.onEvent()
        this._updateUI(this.fishId)
    }

    onEvent() {
        director.on(GameEvent.userFishRemove, this.removeFish, this)
        director.on(GameEvent.fishBeginBeAttack, this.beAttack, this)
        director.on(GameEvent.fishBeginBeAttackShock, this.beAttackShock, this)
        director.on(GameEvent.fishBeginAttack, this.attack, this)
        director.on(GameEvent.fishBeginRecover, this.recover, this)
        director.on(GameEvent.fishBeginEnergyRecover, this.energyRecover, this)
        director.on(GameEvent.fishBeginDead, this.dead, this)
        director.on(GameEvent.fishBeginExplore, this.explore, this)
        director.on(GameEvent.fishBeginEarn, this.earn, this)
        director.on(GameEvent.fishBeginGrowth, this.growth, this)
        director.on(GameEvent.fishBeginStay, this.stay, this)
        // director.on(GameEvent.fishMoveComplete, this.moveComplete, this)
        director.on(GameEvent.fishBeginMove, this.beginMove, this)
        director.on(GameEvent.fishJustMove, this.justMove, this)
        director.on(GameEvent.fishJustBattleMove, this.justBattleMove, this)
        director.on(GameEvent.uiUpdateFish, this._updateUI, this)
        director.on(GameEvent.clearFish, this.clearFish, this)
        director.on(GameEvent.fishBattleComplete, this.battleComplete, this)
        director.on(GameEvent.fishBattleWalk, this.battleWalk, this)
        this.node.on(Node.EventType.TOUCH_END, this._seeMine, this)
    }

    offEvent() {
        director.off(GameEvent.userFishRemove, this.removeFish, this)
        director.off(GameEvent.fishBeginBeAttack, this.beAttack, this)
        director.off(GameEvent.fishBeginBeAttackShock, this.beAttackShock, this)
        director.off(GameEvent.fishBeginAttack, this.attack, this)
        director.off(GameEvent.fishBeginRecover, this.recover, this)
        director.off(GameEvent.fishBeginEnergyRecover, this.energyRecover, this)
        director.off(GameEvent.fishBeginDead, this.dead, this)
        director.off(GameEvent.fishBeginExplore, this.explore, this)
        director.off(GameEvent.fishBeginEarn, this.earn, this)
        director.off(GameEvent.fishBeginGrowth, this.growth, this)
        director.off(GameEvent.fishBeginStay, this.stay, this)
        // director.off(GameEvent.fishMoveComplete, this.moveComplete, this)
        director.off(GameEvent.fishBeginMove, this.beginMove, this)
        director.off(GameEvent.fishJustMove, this.justMove, this)
        director.off(GameEvent.fishJustBattleMove, this.justBattleMove, this)
        director.off(GameEvent.uiUpdateFish, this._updateUI, this)
        director.off(GameEvent.clearFish, this.clearFish, this)
        director.off(GameEvent.fishBattleComplete, this.battleComplete, this)
        director.off(GameEvent.fishBattleWalk, this.battleWalk, this)
        this.node.off(Node.EventType.TOUCH_END, this._seeMine, this)
    }

    async _seeMine() {
        const willShowFish = userInfo.loadFishById(this.fishId!)
        if (willShowFish) {
            const fishInfoUI = lobby.instance.addUI("prefab/fish-info")
            fishInfoUI.getComponent(FishInfoComp)!.changeFish(willShowFish.id)
        } else {
            lobby.instance.showToast('目标已死亡!')
        }

    }

    walkTo(offsetX: number, offsetY: number, duration: number, action: FishGameAction, walkCompleteCallback?: () => void): void {
        const moveClip = this.calcMoveClipByTargetPos(offsetX, offsetY, duration)
        let callback = () => {
            if (this.gameState != FishGameStatus.DEAD) {
                this.gameAction = FishGameAction.NOTING
                this.fishShow.getComponent(Animation)!.play(this.fishShow.idleName())
            }
        }
        if (walkCompleteCallback) {
            callback = walkCompleteCallback
        }
        this.fishShow.notificationPlay(new FishDoSomethingEvent(action,
                () => this.gameState,
                moveClip,
                macro.REPEAT_FOREVER,
                () => {
                },
                () => new Vec3(offsetX, offsetY),
                duration,
                callback,
                0.3
            )
        )
    }


    randomMove(action: FishGameAction, walkCompleteCallback?: () => void) {
        const y = 50 + Math.random() * 50
        let x = -50 + Math.random() * 100
        const minX = 0 - this.node.x
        const maxX = this.getComponent(UITransform)!.width - this.node.x
        x = Math.max(minX, x)
        x = Math.min(maxX, x)
        return this.walkTo(x, y, 0.8, action, walkCompleteCallback)
    }

    moveTo(offsetX: number, offsetY: number, duration: number, moveCallback: () => void) {
        const originPos = this.node.position;
        const originV2Pos = new Vec2()
        originV2Pos.x = originPos.x;
        originV2Pos.y = originPos.y;
        const toward = new Vec2(offsetX, offsetY)
        const force = toward.length() / duration
        this.moveTasks = []
        this.moveTasks.push(new FishMoveTask(originPos, new Vec3(originPos.x + offsetX, originPos.y + offsetY, originPos.z), force, 0, this.gameState != FishGameStatus.IN_BATTLE, 1, duration, moveCallback))
        this.latestMoveAtMs = serverTimeHelper.loadServerTime()
    }

    stay(event: FishStayReply) {
        if (!this.inClear && this.fishId?.eq(event.fishId)) {
            this._fakeExploreCountDown++
            const fakeExploreRate = this.wander ? 16 : 8
            if (this._fakeExploreCountDown % fakeExploreRate == fakeExploreRate - 1 && this.gameState != FishGameStatus.IN_BATTLE) {
                this.doExplore(false, ['一无所获'])
            }
        }
    }

    beginMove(event: FishBeginMoveEvent) {
        if (!this.inClear && this.fishId?.eq(event.fishId)) {
            let callback = () => {
                if (this.gameState != FishGameStatus.DEAD) {
                    this.gameAction = FishGameAction.NOTING
                    this.fishShow.getComponent(Animation)!.play(this.fishShow.idleName())
                    director.emit(GameEvent.fishJustMove, this.fishId)
                }
            }
            this.randomMove(FishGameAction.MOVE_2_ZONE, callback)
        }
    }

    justMove(fishId: Long) {
        if (fishId.neq(this.fishId)) return
        const ff = userInfo.fishInfo.loadFishById(this.fishId)
        if (!ff || ff.status != FishStatus.moving) return
        let callback = () => {
            if (this.gameState != FishGameStatus.DEAD) {
                this.gameAction = FishGameAction.NOTING
                this.fishShow.getComponent(Animation)!.play(this.fishShow.idleName())
                director.emit(GameEvent.fishJustMove, fishId)
            }
        }
        this.randomMove(FishGameAction.MOVE_2_ZONE, callback)
    }

    justBattleMove(fishId: Long) {
        if (fishId.neq(this.fishId)) return
        if (this.gameState != FishGameStatus.IN_BATTLE || !this.battleTarget || !this.battleTarget.isValid) return
        let callback = () => {
            if (this.gameState != FishGameStatus.DEAD) {
                this.gameAction = FishGameAction.NOTING
                this.fishShow.getComponent(Animation)!.play(this.fishShow.idleName())
            }
            if (this.gameState == FishGameStatus.IN_BATTLE) {
                director.emit(GameEvent.fishJustBattleMove, fishId)
            }
        }
        this.battleMove(FishGameAction.RANDOM_MOVE, callback)
    }

    earn(event: FishEarnReply) {
        if (!this.inClear && this.fishId?.eq(event.fishId)) {
            if (this && this.gameState == FishGameStatus.EMPTY) this.randomMove(FishGameAction.RANDOM_MOVE)
        }
    }

    dead(event: FishDeadReply) {
        if (this.fishId?.eq(event.fishId)) {
            this.moveTasks = []
            this.fishShow.getComponent(Animation)!.once(Animation.EventType.FINISHED, () => {
                if (this.mine) director.emit(GameEvent.battleEnd, new BattleEndEvent(this.fishId!, false))
            })
            this.gameState = FishGameStatus.DEAD
            this.fishShow.getComponent(Animation)!.play(this.fishShow.deathName())
            this.progressbar.node.active = false
            if (userInfo.fishInfo.currentShowFishInBattle?.eq(this.fishId)) director.emit(GameEvent.uiChangeAliveDuration, Long.fromNumber(-1))
        }
    }

    explore(event: FishExploreReply) {
        if (!this.inClear && this.fishId?.eq(event.fishId)) {
            this._fakeExploreCountDown = 0
            const cff = userInfo.fishInfo.loadFishById(this.fishId)
            let propWeight = 0
            if (cff) cff.assetInfo!.props.forEach((_, num) => propWeight + num.toNumber())
            if (propWeight < 500) {
                const pns: string[] = cmm.map2array(event.prop, (k, v) => propsConfigsLoader.findPropConfigs(k.toNumber())?.name || '')
                this.doExplore(true, pns)
            } else {
                this.doExplore(false, ['行囊已满'])
            }
        }
    }

    doExplore(success: boolean, propName: string[]) {
        const minePos = this.node.worldPosition
        const willGo = this.node.parent!.getComponent(BattleInGame)!.battleField.nearestWorldAssetInMap(minePos)
        if (!willGo) return
        willGo.blocked = true
        const targetPos = willGo.ceilNode.worldPosition
        const offsetX = targetPos.x - minePos.x
        const offsetY = targetPos.y - minePos.y
        const moveClip = this.calcMoveClipByTargetPos(offsetX, offsetY, 0.7)
        this.fishShow.notificationPlay(new FishDoSomethingEvent(
            success ? FishGameAction.GO_TO_EXPLORE : FishGameAction.GO_TO_FAKE_EXPLORE,
            () => this.gameState,
            moveClip,
            macro.REPEAT_FOREVER,
            () => {
            },
            () => new Vec3(offsetX, offsetY, 0),
            0.7,
            () => {
                this.fishShow.notificationPlay(new FishDoSomethingEvent(
                    success ? FishGameAction.EXPLORE : FishGameAction.FAKE_EXPLORE,
                    () => this.gameState,
                    this.fishShow.digName(),
                    3,
                    () => {
                        this.gameAction = FishGameAction.NOTING
                        this.fishShow.getComponent(Animation)!.play(this.fishShow.idleName())
                        willGo.worldAsset.active = false
                        willGo.hasItem = false
                        willGo.blocked = false
                        let delay = 0
                        for (let pn of propName) {
                            this.scheduleOnce(() => {
                                const damageNode = instantiate(resources.get("prefab/battle/common/damage-number", Prefab)!);
                                const damageComp = damageNode.getComponent(DamageNumber)!;
                                this.node.addChild(damageNode);
                                damageComp.init(success ? '获得材料: ' + pn : pn, this.fishShow.node.position, DamageType.EXPLORE_TYPE);
                            }, delay)
                            delay += 0.1
                        }
                    },
                    () => new Vec3(0, 0, 0),
                    0.01,
                    () => {
                    },
                    0.3
                ))
            },
            0.3
        ))
    }

    calcMoveClipByTargetPos(offsetX: number, offsetY: number, duration: number) {
        const distance = Vec2.distance(new Vec2(0, 0), new Vec2(offsetX, offsetY))
        return this.calcMoveClipBySpeed((distance / duration))
    }

    calcMoveClipBySpeed(speed: number) {
        if (speed < 300) {
            return this.fishShow.walkName()
        }
        return this.fishShow.runningName()
    }

    removeFish(fish: FreshImmortal) {
        if (this.fishId! == fish.id) {
            this.node.active = false
        }
    }

    battleWalk(event: FishBattleWalkEvent) {
        if (!this.inClear && this.fishId?.eq(event.fishId) && this.battleTarget && this.gameState == FishGameStatus.PRE_BATTLE) {
            this.gameState = FishGameStatus.IN_BATTLE
            let callback = () => {
                if (this.gameState != FishGameStatus.DEAD) {
                    this.gameAction = FishGameAction.NOTING
                    this.fishShow.getComponent(Animation)!.play(this.fishShow.idleName())
                }
                if (this.gameState == FishGameStatus.IN_BATTLE) {
                    director.emit(GameEvent.fishJustBattleMove, this.fishId)
                }
            }
            this.battleMove(FishGameAction.RANDOM_MOVE, callback)
        }
    }

    battleMove(action: FishGameAction, walkCompleteCallback?: () => void) {
        if (this.gameState != FishGameStatus.IN_BATTLE || !this.battleTarget || !this.battleTarget.isValid) return
        // const offsetX = 50 + Math.random() * 150
        // const offsetY = -50 - Math.random() * 150
        const offsetX = 50
        const offsetY = -50
        const ps = utils.calculatePerpendicularPoint(this.node.x, this.node.y, this.battleTarget!.node.x, this.battleTarget!.node.y, offsetX, offsetY)
        let x = ps.x
        const y = ps.y > 0 ? ps.y * 1.1 : ps.y * 0.8
        const minX = 0 - this.node.x
        const maxX = this.getComponent(UITransform)!.width - this.node.x
        x = Math.max(minX, x)
        x = Math.min(maxX, x)
        return this.walkTo(x, y, 0.8 + 0.8 * Math.random(), action, walkCompleteCallback)
    }

    battleComplete(event: FishBattleCompleteReply) {
        if (event.fishId.neq(this.fishId)) return
        this.battleTarget = null
        if (this.gameState == FishGameStatus.IN_BATTLE || this.gameState == FishGameStatus.PRE_BATTLE) this.gameState = FishGameStatus.EMPTY
    }

    beAttackShock(event: FishBeAttackShockEvent) {
    }

    beAttack(attackEvent: FishAttackRecordReply) {
        if (!this.inClear && this.fishId?.eq(attackEvent.defenceId)) {
            if (attackEvent.success) {
                if (attackEvent.physicalDamage.toNumber() > 0) {
                    const damageNode = instantiate(resources.get("prefab/battle/common/damage-number", Prefab)!);
                    this.node.addChild(damageNode);
                    const damageComp = damageNode.getComponent(DamageNumber)!;
                    if (!attackEvent.ct) {
                        damageComp.init(attackEvent.physicalDamage.toString(), this.fishShow.node.position, DamageType.NORMAL_DAMAGE_TYPE);
                    } else {
                        damageComp.init(attackEvent.physicalDamage.toString(), this.fishShow.node.position, DamageType.CT_NORMAL_DAMAGE_TYPE);
                    }
                }
                if (attackEvent.realDamage.toNumber() > 0) {
                    const damageNode = instantiate(resources.get("prefab/battle/common/damage-number", Prefab)!);
                    this.node.addChild(damageNode);
                    const damageComp = damageNode.getComponent(DamageNumber)!;
                    damageComp.init(attackEvent.realDamage.toString(), this.fishShow.node.position, DamageType.REAL_DAMAGE_TYPE);
                }
                if (attackEvent.goldDamage.toNumber() > 0) {
                    const damageNode = instantiate(resources.get("prefab/battle/common/damage-number", Prefab)!);
                    this.node.addChild(damageNode);
                    const damageComp = damageNode.getComponent(DamageNumber)!;
                    damageComp.init(attackEvent.goldDamage.toString(), this.fishShow.node.position, DamageType.GOLD_DAMAGE_TYPE);
                }
                if (attackEvent.woodDamage.toNumber() > 0) {
                    const damageNode = instantiate(resources.get("prefab/battle/common/damage-number", Prefab)!);
                    this.node.addChild(damageNode);
                    const damageComp = damageNode.getComponent(DamageNumber)!;
                    damageComp.init(attackEvent.woodDamage.toString(), this.fishShow.node.position, DamageType.WOOD_DAMAGE_TYPE);
                }
                if (attackEvent.waterDamage.toNumber() > 0) {
                    const damageNode = instantiate(resources.get("prefab/battle/common/damage-number", Prefab)!);
                    this.node.addChild(damageNode);
                    const damageComp = damageNode.getComponent(DamageNumber)!;
                    damageComp.init(attackEvent.waterDamage.toString(), this.fishShow.node.position, DamageType.WATER_DAMAGE_TYPE);
                }
                if (attackEvent.fireDamage.toNumber() > 0) {
                    const damageNode = instantiate(resources.get("prefab/battle/common/damage-number", Prefab)!);
                    this.node.addChild(damageNode);
                    const damageComp = damageNode.getComponent(DamageNumber)!;
                    damageComp.init(attackEvent.fireDamage.toString(), this.fishShow.node.position, DamageType.FIRE_DAMAGE_TYPE);
                }
                if (attackEvent.earthDamage.toNumber() > 0) {
                    const damageNode = instantiate(resources.get("prefab/battle/common/damage-number", Prefab)!);
                    this.node.addChild(damageNode);
                    const damageComp = damageNode.getComponent(DamageNumber)!;
                    damageComp.init(attackEvent.earthDamage.toString(), this.fishShow.node.position, DamageType.EARTH_DAMAGE_TYPE);
                }
            } else {
                const damageNode = instantiate(resources.get("prefab/battle/common/damage-number", Prefab)!);
                this.node.addChild(damageNode);
                const damageComp = damageNode.getComponent(DamageNumber)!;
                damageComp.init('0', this.fishShow.node.position, DamageType.DODGE_TYPE);
            }
        }
    }

    attack(attackEvent: FishAttackRecordReply) {
        if (!this.inClear && this.fishId?.eq(attackEvent.attackId)) {
            if (attackEvent.defenceNormalHeal.toNumber() <= 0) {
                // todo 获胜动画
                this.gameState = FishGameStatus.EMPTY
                if (!this.mine) {
                    // todo 敌人击败我方弟子后，自行离开
                    // this.node.active = false;
                    // this.fishId = null;
                } else {
                    director.emit(GameEvent.battleEnd, new BattleEndEvent(attackEvent.defenceId, true))
                }
            }
        }
    }


    // 子弹发射方法
    fireBullet(event: FishAttackRecordReply, target: FishInPool) {
        if (!this || !this.node?.isValid || !target || !target.node?.isValid) return
        const ff = userInfo.fishInfo.loadFishById(this.fishId)!
        if (Vec2.distance(this.node.position, target.node.position) <= 60) {
            this.fishShow.attackEvents.push(event)
            this.fishShow.notificationPlay(new FishDoSomethingEvent(
                FishGameAction.ATTACK,
                () => this.gameState,
                this.fishShow.attackName(),
                1,
                () => {
                    this.gameAction = FishGameAction.NOTING
                    this.fishShow.getComponent(Animation)!.play(this.fishShow.idleName())
                    director.emit(GameEvent.fishJustMove, this.fishId)
                },
                () => new Vec3(0, 0, 0),
                0.01,
                () => {
                },
                ff.battleInfo!.attackInterval.toNumber()
            ))
        } else {
            const bullet = instantiate(resources.get("prefab/battle/skills/normal-attack-comp", Prefab)!)
            if (this.node) {
                this.node.addChild(bullet);
                // 设置初始位置
                // bullet.worldPosition = this.node.worldPosition
                // 获取并初始化子弹组件
                const bulletComp = bullet.getComponent(NormalAttackComp)!;
                bulletComp.init(event, target.node);
            } else {
                bullet.destroy()
            }
        }
    }

    growth(event: FishGrowthReply) {
        if (!this.inClear && this.fishId?.eq(event.fishId)) {
            const fd = userInfo.loadFishById(this.fishId)
            if (fd) {
                const damageNode = instantiate(resources.get("prefab/battle/common/damage-number", Prefab)!);
                this.node.addChild(damageNode);
                const damageComp = damageNode.getComponent(DamageNumber)!;
                damageComp.init('境界突破!', this.fishShow.node.position, DamageType.LEVEL_UP_TYPE);
            }
        }
    }

    energyRecover(event: FishMagicPowerRecoverReply) {
        if (!this.inClear && this.fishId?.eq(event.fishId)) {
            const ff = userInfo.loadFishById(this.fishId)
            if (ff) {
                if (event.currentMagicPower.lte(ff.battleInfo!.currentMagicPower)) {
                    return
                }
                let recoverMagicPower = event.currentMagicPower.sub(ff.battleInfo!.currentMagicPower).toNumber()
                const damageNode = instantiate(resources.get("prefab/battle/common/damage-number", Prefab)!);
                this.node.addChild(damageNode);
                const damageComp = damageNode.getComponent(DamageNumber)!;
                damageComp.init(recoverMagicPower.toString(), this.fishShow.node.position, DamageType.NORMAL_MAGIC_POWER_RECOVER_TYPE);
            }
        }
    }


    recover(event: FishRecoverReply) {
        if (!this.inClear && this.fishId?.eq(event.fishId)) {
            const ff = userInfo.loadFishById(this.fishId)
            if (ff) {
                if (event.currentNormalHealth.lte(ff.battleInfo!.currentNormalHealth)) {
                    return
                }
                let recoverHp = event.currentNormalHealth.sub(ff.battleInfo!.currentNormalHealth).toNumber()
                const damageNode = instantiate(resources.get("prefab/battle/common/damage-number", Prefab)!);
                this.node.addChild(damageNode);
                const damageComp = damageNode.getComponent(DamageNumber)!;
                damageComp.init(recoverHp.toString(), this.fishShow.node.position, event.recoverType == 2 ? DamageType.SUCK_BLOOD_RECOVER_TYPE : DamageType.NORMAL_RECOVER_TYPE);
            }
        }
    }

    protected onDestroy(): void {
        this.offEvent()
    }

    _updateUI(fishId: Long) {
        if (this.fishId?.eq(fishId)) {
            const ff = userInfo.loadFishById(this.fishId)
            if (ff) {
                console.log('fish in pool heal progress: ', ff!.battleInfo!.currentNormalHealth.toNumber() / maxNormalHealth(ff!.battleInfo!.finalBattleInfo!))
                const currentHeal = Math.max(ff!.battleInfo!.currentNormalHealth.toNumber(), 0)
                const maxHeal = maxNormalHealth(ff!.battleInfo!.finalBattleInfo!)
                this.progressbar.progress = currentHeal / maxHeal;
                const iconPath = configsHelper.loadRealmIcon(ff.assetInfo!.level)
                if (!iconPath) this.realmShow.node.active = false
                else {
                    this.realmShow.node.active = true
                    this.realmShow.spriteFrame = resources.get(iconPath, SpriteFrame)!
                }
            }
        }

    }

    listenSelf() {
        const ff = userInfo.loadFishById(this.fishId)
        if (ff) director.emit(GameEvent.userFishActionListen, new UserFishListenEvent(this.fishId, ff.zoneId.toString()))
    }

    clearFish(event: ClearFishEvent) {
        const fishId = event.fishId
        if (this.fishId.neq(fishId)) return
        console.log('try clear wander', fishId)
        if (this.gameState == FishGameStatus.EMPTY) {
            this.inClear = true
            const mc = lobby.instance.getMapCamera()
            const oy = -100 + Math.random() * 200
            const screenHeight = this.getComponent(UITransform)!.height
            const screenWidth = this.getComponent(UITransform)!.width
            if (this.node.worldPositionX < mc.node.worldPositionX) {
                this.walkTo(-(screenHeight / 2), oy, 2, FishGameAction.WANDER_COMPLETE)
            } else {
                this.walkTo(screenWidth / 2, oy, 2, FishGameAction.WANDER_COMPLETE)
            }
            this.scheduleOnce(() => {
                console.log('do clear wander', this)
                this.node.destroy()
            }, 2.5)
        } else if (this.gameState == FishGameStatus.DEAD) {
            this.inClear = true
            this.node.destroy()
        }
    }

    // // 判断速度是否接近0
    // isVelocityZero(): boolean {
    //     if (!this.rigidBody) return true;
    //     const velocity = this.safeLinearVelocity()
    //     if (!this.wander) console.log('current speed', velocity.length())
    //     return velocity.length() != 0 && velocity.length() < 1;
    // }

    // safeLinearVelocity() {
    //     try {
    //         this.currentVelocity = this.rigidBody.linearVelocity
    //     } catch (e) {
    //     }
    //     return this.currentVelocity
    // }

    roleShouldInScreen() {
        if (!this.wander && (this.node.x < 0 || this.node.x > this._screenWidth)) {
            this.node.setPosition(new Vec3(Math.max(0, Math.min(this.node.x, this._screenWidth)), this.node.y, this.node.z))
        }
    }

    cameraShouldFollowRole() {
        let box = this.node.getComponent(UITransform)!.getBoundingBoxToWorld();
        const mc = lobby.instance.getMapCamera()
        let mcBox = mc.node.getComponent(UITransform)!.getBoundingBoxToWorld();
        if (!mcBox.containsRect(box) && this.gameState == FishGameStatus.DEAD) {
            this.node.destroy()
        }
    }

    update(deltaTime: number) {
        this.cameraShouldFollowRole()
        this.roleShouldInScreen()
        if (this.battleTarget && this.battleTarget.node?.isValid) {
            this.fishShow.node.setScale(Math.abs(this.fishShow.node.scale.x) * ((this.battleTarget.node.x - this.node.x) < 0 ? -1 : 1), this.fishShow.node.scale.y)
        }
        if (this.moveTasks.length > 0) {
            const target = this.moveTasks[this.moveTasks.length - 1]
            if (target.changeFaceToward) this.fishShow.node.setScale(Math.abs(this.fishShow.node.scale.x) * ((target.targetPos.x - this.node.x) < 0 ? -1 : 1), this.fishShow.node.scale.y)
            target.moveDurationRemain -= deltaTime
            if ((target.completeType == 1 && (Vec2.distance(target.targetPos, this.node.position) <= 10 || target.moveDurationRemain < -1 * target.moveDuration)) || (target.completeType == 2 && target.moveDurationRemain <= 0)) {
                console.log('latest move cost:', serverTimeHelper.loadServerTime() - this.latestMoveAtMs, 'ms')
                this.latestMoveAtMs = 0
                this.currentMoveTask = null
                this.moveTasks.pop()
                target.moveCallback()
            } else {

                // 1. 计算动态方向向量
                const targetPos = target.targetPos.clone();
                const nowPos = this.node.position.clone();
                let direction = targetPos.subtract(nowPos).normalize();
                // 2. 更新位置（速度恒定）
                this.node.setPosition(new Vec3(nowPos.x + direction.x * target.speed * deltaTime, nowPos.y + direction.y * target.speed * deltaTime, nowPos.z))
            }
        }
    }
}


